#include "cg.h"
#include "../basics/error_handle.h"
#include "cg_utils.h"
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
static int cpuset_next_cpu_to_alloc = 0;
static int total_cpu;

int create_cgroup(int container_id) {
    char cgroupPath[MAX_PATH_LENGTH];
    sprintf(cgroupPath, "%s/%d.slice", main_path, container_id);
    if (access(cgroupPath, F_OK) == 0) {
        return 0;
    }
    int create_cgroup_result = mkdir(cgroupPath, 0755);

    return create_cgroup_result;
}

int delete_cgroup(int container_id) {
    char cgroupPath[MAX_PATH_LENGTH];
    sprintf(cgroupPath, "%s/%d.slice", main_path, container_id);
    int delete_cgroup_status = rmdir(cgroupPath);
    return delete_cgroup_status;
}

int change_cgroup_field(char *cgroupPath, char *field, char *data) {
    char path[MAX_PATH_LENGTH];
    sprintf(path, "%s/%s", cgroupPath, field);
    FILE *file = fopen(path, "r+");
    check(file != NULL);
    fprintf(file, "%s", data);
    fclose(file);
    return 0;
}

int apply_to_cgroup(int container_id, int pid) {
    char path[MAX_PATH_LENGTH];
    sprintf(path, "%s/%d.slice/cgroup.procs", main_path, container_id);
    FILE *file = fopen(path, "a");
    check(file != NULL);
    fprintf(file, "%d\n", pid);

    fclose(file);
    return 0;
}

struct subgroup *create_subgroup(int conatiner_id, unsigned int pid) {
    struct subgroup *new_subgroup = malloc(sizeof(struct subgroup));
    if (new_subgroup == NULL) {
        printf("subgroup init fails:error malloc\n");
        return NULL;
    }

    new_subgroup->container_id = conatiner_id;
    new_subgroup->pid = pid;
    new_subgroup->cpus = 0;
    new_subgroup->cpu_share = 100;
    new_subgroup->mem_limit = 0;
    sprintf(new_subgroup->cgroupPath, "%s/%d.scpoe", main_path, conatiner_id);
    printf("path:%s\n", new_subgroup->cgroupPath);
    check(create_cgroup(conatiner_id) == 0);

    if (pid != 0) {

        check(apply_to_cgroup(conatiner_id, pid) == 0);
    }
    return new_subgroup;
}

// cgroump manager api
int check_controller(char *path, char *controller) {
    FILE *file = fopen(path, "r");
    // printf("%s\n",path);
    check(file != NULL);
    char *buffer = NULL;
    size_t len = 0;
    check(getline(&buffer, &len, file) >= 0);
    check(!ferror(file));

    // printf("check_controller:%s\n",buffer);
    if (strstr(buffer, controller) != NULL) {
        return 1;
    } else {
        return 0;
    }
}

void enable_controller(char *controller) {
    char check_path[MAX_PATH_LENGTH];
    sprintf(check_path, "%s/cgroup.subtree_control", ROOT_PATH);
    int enabled = check_controller(check_path, controller);
    char data[10];
    sprintf(data, "+%s", controller);
    if (enabled == 0) {
        change_cgroup_field(ROOT_PATH, "cgroup.subtree_control", data);
    }
    sprintf(check_path, "%s/cgroup.subtree_control", main_path);
    enabled = check_controller(check_path, controller);
    if (enabled == 0) {
        change_cgroup_field(main_path, "cgroup.subtree_control", data);
    }
    return;
}

int modify_cpu_share(int container_id, unsigned int percent) {
    if (percent > 100 || percent <= 0) {
        return -1;
    }
    char data[10];
    sprintf(data, "%d", 1000 * percent);
    char path[MAX_PATH_LENGTH];
    sprintf(path, "%s/%d.slice", main_path, container_id);
    int change_result = change_cgroup_field(path, "cpu.max", data);
    if (change_result != 0) {
        return change_result;
    }
    return 0;
}

int modify_mem_limit(int conatiner_id, unsigned int memory_size_mb) {
    char data[10];
    sprintf(data, "%dM", memory_size_mb);

    char path[MAX_PATH_LENGTH];
    sprintf(path, "%s/%d.slice", main_path, conatiner_id);
    int change_result = change_cgroup_field(path, "memory.max", data);
    return change_result;
}

int modify_cpus(int conatiner_id, unsigned int number) {
    char data[20];
    total_cpu = sysconf(_SC_NPROCESSORS_ONLN);
    //  cpus to set is more than  total cpu in this computer.
    //  which is not allowed
    if (number > total_cpu || number <= 0) {
        return -1;
    }

    if (number == 1) {
        sprintf(data, "%d", cpuset_next_cpu_to_alloc);
        cpuset_next_cpu_to_alloc++;
    } else if (cpuset_next_cpu_to_alloc + number > total_cpu) {
        unsigned int left = (cpuset_next_cpu_to_alloc + number) % total_cpu;
        sprintf(data, "0-%d,%d-%d", left - 1, cpuset_next_cpu_to_alloc,
                total_cpu - 1);
        cpuset_next_cpu_to_alloc = left;
    } else {
        sprintf(data, "%d-%d", cpuset_next_cpu_to_alloc,
                cpuset_next_cpu_to_alloc + number - 1);
        cpuset_next_cpu_to_alloc += number;
    }
    cpuset_next_cpu_to_alloc %= total_cpu;

    char path[MAX_PATH_LENGTH];
    sprintf(path, "%s/%d.slice", main_path, conatiner_id);
    int change_result = change_cgroup_field(path, "cpuset.cpus", data);
    printf("[UPDATE] CPUS DATA:%s\n", data);
    return change_result;
}
